// locutor
// (c) 2009, Normen Müller, normen.mueller@gmail.com  
// $Id: XGRS.scala 31 2009-11-12 12:05:13Z normen.mueller $
package locutor.parsing

import collection.mutable.HashSet

/** Companion to <code>XGRSParser</code> class.
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.3 */
object XGRSParser {
  
  val reserved = new HashSet[String] ++ List(
    "def"
  ) ++ BaseParser.reserved
  
  val delimiters = new HashSet[String] ++ List(
    "!", "$", "&&", "&", "||", "|", "<", ">", "(", ")", "%", "?", "^"
  ) ++ BaseParser.delimiters
  
}

/** Primitive GrGEN.NET extended graph sequences (xgrs) parser.
 *
 * At the moment only primitive string representations of GrGEN xgrs are supported.
 * No need for sophisticated AST yet due to direct looping into GrGEN.
 * 
 * Restrictions:
 * - parallel call rule
 *
 * @thanks GrGEN.NET manual, GrGen.g#(Embedded XGRS), and SequenceParser.csc for syntax rules (v2.5.x) 
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.3 */
trait XGRSParser extends BaseParser {

  lazy val xgrs: Parser[String] = xgrs_lor

  private lazy val xgrs_lor = xgrs_land ~ ((opt("$") ~ "||" ~ xgrs_land)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {
      case Some(flag) ~ op ~ expr => b append flag append op append expr
      case None       ~ op ~ expr => b append op append expr
    })).toString
  }

  private lazy val xgrs_land = xgrs_bor ~ ((opt("$") ~ "&&" ~ xgrs_bor)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {
      case Some(flag) ~ op ~ expr => b append flag append op append expr
      case None       ~ op ~ expr => b append op append expr
    })).toString
  }

  private lazy val xgrs_bor = xgrs_bxor ~ ((opt("$") ~ "|" ~ xgrs_bxor)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {
      case Some(flag) ~ op ~ expr => b append flag append op append expr
      case None       ~ op ~ expr => b append op append expr
    })).toString
  }

  private lazy val xgrs_bxor = xgrs_band ~ ((opt("$") ~ "^" ~ xgrs_band)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {
      case Some(flag) ~ op ~ expr => b append flag append op append expr
      case None       ~ op ~ expr => b append op append expr
    })).toString
  }

  private lazy val xgrs_band = xgrs_unary ~ ((opt("$") ~ "&" ~ xgrs_unary)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {
      case Some(flag) ~ op ~ expr => b append flag append op append expr
      case None       ~ op ~ expr => b append op append expr
    })).toString
  }

  private lazy val xgrs_unary: Parser[String] = ("!" ~> xgrs_unary ^^ {case ss => "!"+ss} | xgrs_single)

  private lazy val xgrs_single = xgrs_simple ~ opt("*" | "+" | range) ^^ { case ss ~ m => ss+(m match {case Some(m) => m case _ => ""}) }

  private lazy val xgrs_simple =
    ( xgrs_assign
    | xgrs_exec
    | ("def" ~ "(") ~> xgrs_params <~ ")" ^^ {case ps    => "def("+ps+")"}
    | opt("%") ~ "true"                   ^^ {case d ~ t => (d match {case Some(d) => d case _ => ""})+t}
    | opt("%") ~ "false"                  ^^ {case d ~ f => (d match {case Some(d) => d case _ => ""})+f}
    | "(" ~> xgrs <~ ")"                  ^^ {case ss    => "("+ss+")"}
    | "<" ~> xgrs <~ ">"                  ^^ {case ss    => "<"+ss+">"}
    )

  private lazy val xgrs_exec = 
    ( opt("(" ~> xgrs_vars <~ (")" ~ "=")) ~ opt("$" ~> opt(numeric)) ~ ("[" ~> opt("%")) ~ opt("?") ~ (xgrs_rule <~ "]") ^^ {
      case vs ~ n ~ d ~ q ~ r =>
        (vs match {case Some(vs) => "("+vs+")=" case _ => ""}) +
        (n match {case Some(n) => n match {case Some(n) => "$"+n case _ => "$"} case _ => ""}) +
        "["+(d match {case Some(d) => d case _ => ""}) + (q match {case Some(q) => q case _ => ""}) + r + "]"
    }
    | opt("(" ~> xgrs_vars <~ (")" ~ "=")) ~ opt("%") ~ opt("?") ~ xgrs_rule ^^ {
      case vs ~ d ~ q ~ r =>
        (vs match {case Some(vs) => "("+vs+")=" case _ => ""}) +
        (d match {case Some(d) => d case _ => ""}) + (q match {case Some(q) => q case _ => ""}) + r        
    }
    )

  private lazy val xgrs_rule = ident ~ opt("(" ~> xgrs_rule_params <~ ")") ^^ {
    case i ~ Some(ps) => i+"("+ps+")"
    case i ~ None     => i
  }

  private lazy val xgrs_assign = ident ~ opt(":" ~> ident) ~
    ("=" ~> (ident | numeric
            | "@" ~ "(" ~ ident ~ ")" ^^ {case i => "@("+i+")"}
            | "true" | "false"
            | "(" ~> xgrs <~ ")" ^^ {case ss => "("+ss+")"}
            )
    ) ^^ {
      case i ~ t ~ v => i+(t match {case Some(t) => ":"+t case _ => ""})+"="+v
    }

  private lazy val xgrs_params = ident ~ (("," ~ ident)*) ^^ { case hd ~ tl => (hd :: tl) mkString "," }

  private lazy val xgrs_rule_params = (text | numeric | "true" | "false") ~ (("," ~> (text | numeric | "true" | "false"))*) ^^ {
    case hd ~ tl => (hd :: tl) mkString "," 
  }

  private lazy val xgrs_vars = (ident ~ opt(":" ~> ident)) ~ (("," ~> (ident ~ opt(":" ~> ident)))*) ^^ {
    case hd ~ tl => ((hd :: tl) map {case v ~ Some(t) => v+":"+t case v ~ None => v}) mkString ","
  }

}
